package com.boot.security;

import com.boot.dao.UserDao;
import com.boot.dto.TokenDto;
import com.boot.entity.Permission;
import com.boot.entity.User;
import com.boot.utils.JWTUtil;
import com.boot.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.lang.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.util.Date;
import java.util.List;

/**
 * @author: zcs
 * @data: 2021/4/15
 * @description: 自定义 Token 验证过滤器,客户端登录成功时，后台会把生成的 token 返回给前端，之后客户端每次请求后台接口将会把这个 token 附在 header 头中传递给后台，
后台会验证这个 token 是否有效，如果有效就把用户信息加载至 SpringSecurity 中，如果无效则会跳转至上一步提供 AuthenticationEntryPoint 进行处理。
 */
public class TokenAuthenticationFilter extends OncePerRequestFilter {

//    @Autowired
//    private RedisUtil redisUtil;

    @Autowired
    private UserDao userDao;

    @Autowired
    private JWTUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String tokenStr = request.getHeader("token");
//        if (tokenStr != null && !tokenStr.isEmpty()) {
//            TokenDto tokenCache = (TokenDto)redisUtil.get(tokenStr);
//            if (tokenCache != null) {
//                User user = userDao.selectById(tokenCache.getUserId());
//                if (user == null) {
//                    throw new UsernameNotFoundException("token已失效");
//                }
//                List<Permission> permissions = userDao.selectPermissionsById(user.getId());
//                if (permissions != null){
//                    user.setPermissionList(permissions);
//                }
//
//                user.setToken(tokenStr);
//
//                UsernamePasswordAuthenticationToken authentication =
//                        new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
//                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//                logger.info(String.format("Authenticated user %s, setting security context", user.getUsername()));
//                SecurityContextHolder.getContext().setAuthentication(authentication);
//            }
//        }
        if (tokenStr != null && !tokenStr.isEmpty()) {
            //用JWTUtil来校验token是否正确
            Claims claims = jwtUtil.getTokenClaim(tokenStr);
            if(claims == null || jwtUtil.isTokenExpired(claims.getExpiration())){
                throw new AccessDeniedException(jwtUtil.getHeader() + "token不正确");
            }

            //判断token是否失效
            boolean tokenExpired = jwtUtil.isTokenExpired(claims.getExpiration());
            if(tokenExpired){
                throw new UsernameNotFoundException("token已失效");
            }

            //得到userId，并查询用户信息
            String userIdStr = claims.getSubject();
            User user = userDao.selectById(Long.parseLong(userIdStr));
            Assert.notNull(user,"该用户已移除！");
            List<Permission> permissions = userDao.selectPermissionsById(user.getId());
            if (permissions != null){
                user.setPermissionList(permissions);
            }

            user.setToken(tokenStr);
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            logger.info(String.format("Authenticated user %s, setting security context", user.getUsername()));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }
}
